// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/accelerators/accelerator_manager.h"

#include <algorithm>

#include "base/logging.h"

namespace ui {

AcceleratorManager::AcceleratorManager()
{
}

AcceleratorManager::~AcceleratorManager()
{
}

void AcceleratorManager::Register(const Accelerator& accelerator,
    HandlerPriority priority,
    AcceleratorTarget* target)
{
    AcceleratorTargetList& targets = accelerators_[accelerator].second;
    DCHECK(std::find(targets.begin(), targets.end(), target) == targets.end())
        << "Registering the same target multiple times";

    // All priority accelerators go to the front of the line.
    if (priority) {
        DCHECK(!accelerators_[accelerator].first)
            << "Only one _priority_ handler can be registered";
        targets.push_front(target);
        // Mark that we have a priority accelerator at the front.
        accelerators_[accelerator].first = true;
        return;
    }

    // We are registering a normal priority handler. If no priority accelerator
    // handler has been registered before us, just add the new handler to the
    // front. Otherwise, register it after the first (only) priority handler.
    if (!accelerators_[accelerator].first)
        targets.push_front(target);
    else
        targets.insert(++targets.begin(), target);
}

void AcceleratorManager::Unregister(const Accelerator& accelerator,
    AcceleratorTarget* target)
{
    AcceleratorMap::iterator map_iter = accelerators_.find(accelerator);
    if (map_iter == accelerators_.end()) {
        NOTREACHED() << "Unregistering non-existing accelerator";
        return;
    }

    AcceleratorTargetList* targets = &map_iter->second.second;
    AcceleratorTargetList::iterator target_iter = std::find(targets->begin(), targets->end(), target);
    if (target_iter == targets->end()) {
        NOTREACHED() << "Unregistering accelerator for wrong target";
        return;
    }

    // Check to see if we have a priority handler and whether we are removing it.
    if (accelerators_[accelerator].first && target_iter == targets->begin()) {
        // We've are taking the priority accelerator away, flip the priority flag.
        accelerators_[accelerator].first = false;
    }

    targets->erase(target_iter);
}

void AcceleratorManager::UnregisterAll(AcceleratorTarget* target)
{
    for (AcceleratorMap::iterator map_iter = accelerators_.begin();
         map_iter != accelerators_.end(); ++map_iter) {
        AcceleratorTargetList* targets = &map_iter->second.second;
        targets->remove(target);
    }
}

bool AcceleratorManager::Process(const Accelerator& accelerator)
{
    bool result = false;
    AcceleratorMap::iterator map_iter = accelerators_.find(accelerator);
    if (map_iter != accelerators_.end()) {
        // We have to copy the target list here, because an AcceleratorPressed
        // event handler may modify the list.
        AcceleratorTargetList targets(map_iter->second.second);
        for (AcceleratorTargetList::iterator iter = targets.begin();
             iter != targets.end(); ++iter) {
            if ((*iter)->CanHandleAccelerators() && (*iter)->AcceleratorPressed(accelerator)) {
                result = true;
                break;
            }
        }
    }
    return result;
}

AcceleratorTarget* AcceleratorManager::GetCurrentTarget(
    const Accelerator& accelerator) const
{
    AcceleratorMap::const_iterator map_iter = accelerators_.find(accelerator);
    if (map_iter == accelerators_.end() || map_iter->second.second.empty())
        return NULL;
    return map_iter->second.second.front();
}

bool AcceleratorManager::HasPriorityHandler(
    const Accelerator& accelerator) const
{
    AcceleratorMap::const_iterator map_iter = accelerators_.find(accelerator);
    if (map_iter == accelerators_.end() || map_iter->second.second.empty())
        return false;

    // Check if we have a priority handler. If not, there's no more work needed.
    if (!map_iter->second.first)
        return false;

    // If the priority handler says it cannot handle the accelerator, we must not
    // count it as one.
    return map_iter->second.second.front()->CanHandleAccelerators();
}

} // namespace ui
